package top.dprkshow.practice.arrays;

/**
 * @author Kent HAN
 * @date 2021/8/4
 */
public class MyVector {
    private static final int GROW_FACTOR = 2;
    private static final int SHRINK_FACTOR = 4;
    private static final int MIN_CAPACITY = 16;

    protected int size;
    protected int capacity;
    protected Object[] data;

    MyVector(int capacity) {
        size = 0;
        // get true capacity;
        int trueCapacity = determineCapacity(capacity);
        this.capacity = trueCapacity;
        data = new Object[trueCapacity];
    }

    int determineCapacity(int capacity) {
        if (capacity < 1) {
            throw new RuntimeException("illegal capacity");
        }
        int trueCapacity = MIN_CAPACITY;
        while (capacity > trueCapacity/GROW_FACTOR) {
            trueCapacity *= GROW_FACTOR;
        }
        return trueCapacity;
    }

    int getSize() {
        return size;
    }

    boolean isEmpty() {
        return size == 0;
    }

    int getCapacity() {
        return capacity;
    }

    void push(Object obj) {
        resizeForSize(size + 1);
        data[size] = obj;
        ++size;
    }

    void resizeForSize(int newSize){
        if (newSize < 0) {
            throw new RuntimeException("illegal size");
        }
        if (newSize >= capacity) {
            increaseSize();
        } else if (newSize <= capacity/SHRINK_FACTOR) {
            decreaseSize();
        }
    }

    void increaseSize(){
        Object[] newData = new Object[capacity*GROW_FACTOR];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
        capacity = determineCapacity(capacity);
    }

    void decreaseSize() {
        if (capacity == MIN_CAPACITY) {
            return;
        }
        capacity /= GROW_FACTOR;
        Object[] newData = new Object[capacity];
        for (int i = 0; i < size; i++) {
            newData[i] = data[i];
        }
        data = newData;
    }

    Object getValueAt(int index) {
        if (index < 0 || index > size) {
            throw new RuntimeException("illegal index");
        }
        return data[index];
    }

    Object pop() {
        if (size  == 0) {
            throw new RuntimeException("vector is empty");
        }
        resizeForSize(size - 1);
        Object value = data[size - 1];
        size--;
        return value;
    }

    void insert(Object value, int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("illegal index");
        }
        resizeForSize(size + 1);
        for (int i = size - 1; i < index ; i--) {
            data[i] = data [i - 1];
        }
        data[index] = value;
        size++;
    }

    void prepend(Object value) {
        insert(value, 0);
    }

    void delete(int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("illegal index");
        }
        resizeForSize(size - 1);
        for (int i = index; i < size - 1; i++) {
            data[i] = data[i + 1];
        }
        size--;
    }

    void remove(Object value) {
        for (int i = 0; i < size; i++) {
            if (value.equals(data[i])) {
                delete(i);
                i--;
            }
        }
    }

    int findValue(Object value) {
        for (int i = 0; i < size; i++) {
            if (data[i].equals(value)) {
                return i;
            }
        }
        return -1;
    }

}
